/**
 * 展览相机控制 - WASD移动、拖动鼠标旋转镜头、点击地板快速移动
 */
import * as THREE from 'three';
import gsap from 'gsap';
import merge from 'lodash/merge';

const MOVEMENT_SPEED = 0.1; // 移动速度
const CAMERA_HEIGHT = 1.5; // 固定相机高度
const PICK_GROUND_THRESH = 0.03; // 拾取地面的时候允许的阈值
const LAYER = 1; // 可见图层

const cameraDirection = new THREE.Vector3();
const up = new THREE.Vector3(0, 1, 0);
const cameraUp = up.clone().setLength(MOVEMENT_SPEED);
const cameraRight = new THREE.Vector3();
const textureLoader = new THREE.TextureLoader();
const modelList: THREE.Object3D[] = [];
type TourControlsOptions = {
  fixHeight?: boolean;
  cameraHeight?: number;
  interval?: number;
  raycasterAdjust?: boolean;
};

let rotationMatrix = new THREE.Matrix4();

export default class TourControls {
  scene: THREE.Scene;
  camera: THREE.PerspectiveCamera;
  dom: HTMLElement;
  models: THREE.Object3D[]; // 需要碰撞到的物体
  // 设置
  raycasterAdjust = true; //

  fixHeight = true;
  cameraHeight: number = 1.5;
  interval: number = 1;
  // 点击地面传送
  // planeIndicator: THREE.Mesh;
  raycaster = new THREE.Raycaster();
  mouseDownPos = new THREE.Vector2();
  mouseMovePos = new THREE.Vector2();
  mouseNormPos = new THREE.Vector2();
  pickedPos = new THREE.Vector2();
  picked = false;
  constructor(
    scene: THREE.Scene,
    camera: THREE.PerspectiveCamera,
    dom: HTMLElement,
    models: THREE.Object3D[],
    userOptions?: TourControlsOptions
  ) {
    this.scene = scene;
    this.camera = camera;
    this.dom = dom;
    this.models = models;
    this.raycaster = new THREE.Raycaster(
      this.camera.position,
      cameraDirection,
      0,
      1
    );

    const options = merge({}, userOptions);
    if (options.fixHeight != null) this.fixHeight = options.fixHeight;
    if (options.cameraHeight != null) this.cameraHeight = options.cameraHeight;
    if (options.raycasterAdjust != null)
      this.raycasterAdjust = options.raycasterAdjust;
    if (options.interval != null) this.interval = options.interval;
    // init
    this.camera.rotation.order = 'YXZ';

    // 点击地面传送
    // this.raycaster.layers.set(LAYER);
    // this.planeIndicator = new THREE.Mesh(
    //   new THREE.PlaneGeometry(1, 1),
    //   new THREE.MeshStandardMaterial({
    //     map: textureLoader.load('./tour-controls/position.png'),
    //     transparent: true,
    //   })
    // );
    // this.planeIndicator.position.y = 0.01;
    // this.planeIndicator.rotation.x = THREE.MathUtils.degToRad(-90);
    // scene.add(this.planeIndicator);

    // 鼠标
    this.onMouseDown = this._onMouseDown.bind(this);
    this.onMouseUp = this._onMouseUp.bind(this);
    this.onMouseMove = this._onMouseMove.bind(this);
    this.dom.addEventListener('mousedown', this.onMouseDown);
    this.dom.addEventListener('mouseup', this.onMouseUp);
    this.dom.addEventListener('mousemove', this.onMouseMove);
    // 键盘
    this.onKeyDown = this._onKeyDown.bind(this);
    this.onKeyUp = this._onKeyUp.bind(this);
    document.body.addEventListener('keydown', this.onKeyDown);
    document.body.addEventListener('keyup', this.onKeyUp);
  }

  // 启用、禁用
  enabled = true;

  // 鼠标
  isLmbDown = false; // 左键按下
  onMouseDown: (e: MouseEvent) => void;
  onMouseUp: (e: MouseEvent) => void;
  onMouseMove: (e: MouseEvent) => void;
  _onMouseDown(e: MouseEvent) {
    if (!this.enabled) return;

    this.mouseDownPos.set(e.clientX, e.clientY);
    // console.log('[TourControls] onMouseDown')
    if (e.buttons == 1) this.isLmbDown = true;
  }
  _onMouseUp(e: MouseEvent) {
    if (!this.enabled) return;

    this.mouseMovePos.set(e.clientX, e.clientY);
    const mouseDragMoved = this.mouseDownPos.distanceTo(this.mouseMovePos) > 5;
    if (this.picked && !mouseDragMoved) {
      gsap.to(this.camera.position, {
        x: this.pickedPos.x,
        z: this.pickedPos.y,
      });
      this.picked = false;
    }
    // console.log('[TourControls] onMouseUp')
    this.isLmbDown = false;
  }
  _onMouseMove(e: MouseEvent) {
    if (!this.enabled) return;

    this.mouseMovePos.set(e.clientX, e.clientY);
    // console.log('[TourControls] onMouseMove', this.isMouseLeft)
    if (this.isLmbDown) {
      this.camera.rotation.y += e.movementX / 600;
      this.camera.rotation.x += e.movementY / 600;
    }
  }

  // 键盘
  pressedKeyMap: { [key: string]: boolean } = {};
  onKeyDown: (e: KeyboardEvent) => void;
  onKeyUp: (e: KeyboardEvent) => void;
  _onKeyDown(e: KeyboardEvent) {
    if (!this.enabled) return;
    // console.log('[TourControls] onKeyDown', e.key)
    this.pressedKeyMap[e.key] = true;
  }
  _onKeyUp(e: KeyboardEvent) {
    if (!this.enabled) return;
    // console.log('[TourControls] onKeyUp', e.key)
    this.pressedKeyMap[e.key] = false;
  }

  // 更新：根据按键进行移动
  lastUpdateTime = 0;
  maxFps = 30;
  update() {
    if (Date.now() - this.lastUpdateTime < 1000 / this.maxFps) return;
    this.lastUpdateTime = Date.now();
    //四个方位是否产生碰撞
    let leftCollide = 0;
    let rightCollide = 0;
    let forwardCollide = 0;
    let backCollide = 0;

    cameraDirection.set(0, 0, -1);
    cameraDirection
      .applyQuaternion(this.camera.quaternion)
      .setLength(MOVEMENT_SPEED);
    cameraRight.crossVectors(cameraDirection, up).setLength(MOVEMENT_SPEED);

    // 前后移动
    if (this.pressedKeyMap['w']) {
      forwardCollide = this.collideCheck(0);
      if (!forwardCollide) this.camera.position.add(cameraDirection);
    } else if (this.pressedKeyMap['s']) {
      backCollide = this.collideCheck(180);
      if (!backCollide) this.camera.position.add(cameraDirection);
    }

    // 左右移动
    if (this.pressedKeyMap['a']) {
      leftCollide = this.collideCheck(90);
      if (!leftCollide) this.camera.position.sub(cameraRight);
    } else if (this.pressedKeyMap['d']) {
      rightCollide = this.collideCheck(270);
      if (!rightCollide) this.camera.position.add(cameraRight);
    }

    // 上下移动
    if (this.fixHeight) {
      this.camera.position.y = this.cameraHeight;
    } else {
      if (this.pressedKeyMap['r']) {
        this.camera.position.add(cameraUp);
        // this.updatePlaneIndicator();
      } else if (this.pressedKeyMap['f']) {
        this.camera.position.sub(cameraUp);
        // this.updatePlaneIndicator();
      }
    }
  }

  // 碰撞检测
  collideCheck(angle: number) {
    rotationMatrix.makeRotationY((angle * Math.PI) / 180);
    cameraDirection.set(0, 0, -1);
    cameraDirection
      .applyQuaternion(this.camera.quaternion)
      .setLength(MOVEMENT_SPEED);
    cameraRight.crossVectors(cameraDirection, up).setLength(MOVEMENT_SPEED);
    cameraDirection.applyMatrix4(rotationMatrix);
    if (this.raycasterAdjust) this.raycaster.ray.origin.y -= this.interval; //对射线的起始点进行调整
    const intersections = this.raycaster.intersectObjects(this.models, true);
    return intersections.length;
  }
}
